#include <functional>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <elf.h>
#include <sys/mman.h>
#include <sys/stat.h>

#if __x86_64__

typedef Elf64_Ehdr Elf_Ehdr;
typedef Elf64_Shdr Elf_Shdr;
typedef Elf64_Dyn Elf_Dyn;

#else

typedef Elf32_Ehdr Elf_Ehdr;
typedef Elf32_Shdr Elf_Shdr;
typedef Elf32_Dyn Elf_dyn;
#endif

void err_exit(int x, const char* s)
{
    if(x == -1)
    {
        perror(s);
        exit(1);
    }
}

struct Guard
{
  Guard(std::function<void()>&& c) : cb{std::move(c)} {}
  ~Guard() { cb(); }
  std::function<void()> cb;
};

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        fprintf(stderr, "%s file\n", argv[0]);
        return 1;
    }
    int fd = open(argv[1], O_RDONLY);
    err_exit(fd, "open");
    Guard fdGuard{[fd] { ::close(fd); }};

    struct stat st;
    fstat(fd, &st);

    char* mem = (char*)mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if(mem == MAP_FAILED)
    {
        err_exit(-1, "mmap");
    }
    Guard memGuard{[mem, st] { ::munmap(mem, st.st_size); }};

    // offset 0
    Elf_Ehdr* eh = (Elf_Ehdr*)mem;
    if(eh->e_type != ET_DYN)
    {
        err_exit(-1, "not shared lib");
    }

    if(eh->e_shstrndx == SHN_UNDEF)
    {
        err_exit(-1, "no section name string table");
    }
    // offset 0
    Elf_Shdr* sections = (Elf_Shdr*)&mem[eh->e_shoff];
    int dynIndex = -1;
    int strIndex = -1;
    for(int i = 0; i < eh->e_shnum; ++i)
    {
        if(sections[i].sh_type == SHT_DYNAMIC)
        {
            dynIndex = i;
        }
        if(sections[i].sh_type == SHT_STRTAB)
        {
            strIndex = i;
        }
        if(dynIndex >=0 && strIndex >= 0)
        {
            break;
        }
    }
    if(dynIndex < 0 || strIndex < 0)
    {
      printf("no .dynamic or .dynstr section found!\n");
      return 1;
    }

    char* strTab = (char*)&mem[sections[strIndex].sh_offset];
    Elf_Dyn* dyn = (Elf_Dyn*)&mem[sections[dynIndex].sh_offset];
    char* soname = 0;
    while(dyn->d_tag != DT_NULL)
    {
        if(dyn->d_tag == DT_SONAME)
        {
            soname = &strTab[dyn->d_un.d_val];
            break;
        }
        dyn++;
    }

    if(soname)
    {
      printf("SONAME: %s\n", soname);
    }
    else
    {
      printf("no soname\n");
    }
}

